﻿using System.Linq;
using GammaJul.ReSharper.GammaXaml.Psi.Resolve;
using JetBrains.Annotations;
using JetBrains.ReSharper.Feature.Services.Util;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Resolve.Filters;
using JetBrains.ReSharper.Psi.Resolve;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Psi.Xaml.Tree;
using JetBrains.ReSharper.Psi.Xml.Impl.Tree.References;
using JetBrains.ReSharper.Psi.Xml.Impl.Util;
using JetBrains.ReSharper.Psi.Xml.Tree;
using JetBrains.ReSharper.Psi.Util;

namespace GammaJul.ReSharper.GammaXaml.Psi.Xaml {

	/// <summary>
	/// Represents a XAML reference, usually placed on a x:Name, that matches a method or property name in the data context.
	/// </summary>
	internal class CaliburnBoundXamlReference : XmlQualifiableReferenceWithToken, IXamlReferenceWithToken, IPrefferedReference, ILateBoundReference {
		
		protected override bool AllowedNotResolved {
			get { return true; }
		}

		public override Staticness GetStaticness() {
			return Staticness.Any;
		}

		public override IQualifier GetQualifier() {
			return GetDataContextQualifier();
		}

		[NotNull]
		private DataContextQualifier GetDataContextQualifier() {
			return new DataContextQualifier(myOwner);
		}

		public override ITypeElement GetQualifierTypeElement() {
			return GetDataContextQualifier().GetExpressionType().GetTypeElement<ITypeElement>();
		}

		protected override ISymbolFilter[] GetCompletionFilters() {
			return new ISymbolFilter[] {
				IsMethodOrPropertyFilter.Instance,
				IsPublicFilter.INSTANCE
			};
		}

		public override ISymbolTable GetReferenceSymbolTable(bool useReferenceName) {
			return base.GetReferenceSymbolTable(useReferenceName).Filter(GetSymbolFilters());
		}

		[NotNull]
		internal static CaliburnBoundXamlReference FromDeclarationNameAttribute([NotNull] IDeclarationNameAttribute declName) {
			IXmlAttributeValue value = declName.Value;
			return new CaliburnBoundXamlReference(declName, value != null ? value.ValueToken : null);
		}
		
		protected override IReference BindToInternal(IDeclaredElement declaredElement, ISubstitution substitution) {
			if (!ElementRange.IsValid)
				return this;

			return ReferenceWithTokenUtil.SetText(this, declaredElement.ShortName);
		}

		public override Refers RefersToDeclaredElement(IDeclaredElement declaredElement) {
			IResolveResult resolveResult = Resolve().Result;
			if (Equals(resolveResult.DeclaredElement, declaredElement))
				return Refers.YES;
			return resolveResult.Candidates.Any(element => Equals(element, declaredElement)) ? Refers.MAYBE : Refers.NO;
		}

		protected CaliburnBoundXamlReference([NotNull] ITreeNode owner, [CanBeNull] IXmlValueToken token)
			: base(owner, null, token, token != null ? token.UnquotedValueRange : TreeTextRange.InvalidRange) {
		}

	}

}